#include "file_manager.h"
#include "uvlog.h"
#include "index_manager.h"
#include "utils.h"
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define CLOSE(fd) if(fd != 0){close(fd);fd = 0;}
#define MMAP(fd,offset) mmap(NULL,PER_PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset)

IndexFile::IndexFile(const char* path,const char* prefix,int seq){
	char file[255] = {0};
	//sprintf(file,"%s/%s.idx.%d",path,prefix,seq);
	MAKE_FILE_NAME(file, path, prefix, seq);

	this->seq = seq;
	bzero(theFile,sizeof(theFile));
	strncpy(theFile,file,sizeof(theFile) - 1);
	
	init();
}

IndexFile::~IndexFile(){

	map<int,Page*>::iterator it;
	while((it = pagesMap.begin()) != pagesMap.end()){
		delete it->second;
		pagesMap.erase(it);
	}

	CLOSE(fd);
}

int IndexFile::init(){
	fd = open(theFile,O_RDWR);
	if(fd < 0){
		LOG_ERROR("open file %s failed",theFile);
		return -1;
	}

	LOG_INFO("file %s init success",theFile);

	
	//load all the page into memory,avoid mmap's vma 
	for(int i = 0; i < PER_FILE_SIZE / PER_PAGE_SIZE; ++i){

		if(seq == 0 && i == 0) continue;
		
		IndexFilePage* page = NULL;
		getPage(PER_PAGE_SIZE * i,&page);
	}
	
	LOG_INFO("load file %s into memory success",theFile);
	
	return 0;
}

int IndexFile::getPage(unsigned int offset,IndexFilePage** page){

	if(offset % PER_PAGE_SIZE != 0  || offset > PER_FILE_SIZE || (seq == 0 && offset == 0) ){
		LOG_ERROR("illegal %d file offset value %d",seq,offset);
		return -1;
	}
	
	map<int,Page*>::iterator it = pagesMap.find(offset);
	if(it != pagesMap.end()){
		*page = (IndexFilePage*)it->second;

		//change lru list order
		LRUList::instance()->update(*page);
		
		return 0;
	}
	
	void* addr = NULL;
	do{
		addr = MMAP(fd, offset);
		
		if(addr == MAP_FAILED){
			LOG_ERROR("mmap failed,%m");

			if(errno == ENOMEM){
				/*
				int type = 0;
				if(Utils::getCurSharePageCount() < PER_FILE_SIZE / PER_PAGE_SIZE * 2){
					LOG_DEBUG("now page count=%u",Utils::getCurSharePageCount());
					type = 1;
				}
				*/
				IndexManager::instance()->freePages();
			}
			else{
				LOG_ERROR("mmap failed,%m");
				return -1;
			}
		}
		else{
			break;
		}
	}while(true);

	*page = new IndexFilePage(addr);
	
	if(!*page){
		LOG_ERROR("new IndexFilePage failed,%m");	
		return -1;
	}
	
	(*page)->offset = (uint64_t)seq * PER_FILE_SIZE  + offset;
	pagesMap[offset] = *page;
	
	return 0;
}

int IndexFile::freePage(unsigned int offset){
	if(offset % PER_PAGE_SIZE != 0  || offset > PER_FILE_SIZE){
		LOG_ERROR("illegal %d file offset value %d",seq,offset);
		return -1;
	}

	map<int,Page*>::iterator it = pagesMap.find(offset);
	if(it != pagesMap.end()){
		delete it->second;
		pagesMap.erase(it);
	}else{
		LOG_WARNING("some error occurced,page [%d,%u] not found",seq,offset);
	}

	return 0;
}


int IndexFile::remap(){
	LOG_DEBUG("file %d remap now",seq);
	if(pagesMap.size() < 2){
		return 0;
	}

	map<int,Page*>::iterator it;
	for(it = pagesMap.begin();it != pagesMap.end();++it){
		delete it->second;
	}
	
	map<int,Page*> bakMap(pagesMap.begin(),pagesMap.end());
	pagesMap.clear();
	
	for(it = bakMap.begin(); it != bakMap.end(); ++it){
		IndexFilePage* page = NULL;
		getPage(it->first,&page);
	}

	return 0;
}

int IndexFile::getHeadPage(IndexFileHeader** page){
	if(seq != 0){
		LOG_ERROR("only file 0 can get head page,this is %d",seq);
		return -1;
	}

	void * addr = MMAP(fd,0);
	if(addr == MAP_FAILED){
		LOG_ERROR("mmap head page failed,%m");
		return -1;
	}

	*page = new IndexFileHeader(addr);
	
	if(!*page){
		LOG_ERROR("maybe memory exhaust");
		return -1;
	}
	
	return 0;
}

Page::Page(void* addr)
{
	mappedAddr = (char*) addr;
	lastAccessTime = 0;
	isDirty = false;
	lruPos = LRUList::instance()->lp.end();
	dirtyPos = DirtyList::instance()->lp.end();
}

Page::~Page(){
	if(mappedAddr){
		munmap(mappedAddr,PER_PAGE_SIZE);
		mappedAddr = NULL;

		LRUList::instance()->remove(this);
		DirtyList::instance()->remove(this);
	}
}

int Page::flush(){
	if(mappedAddr && isDirty){
		msync(mappedAddr,PER_PAGE_SIZE,MS_SYNC);
		isDirty = false;
		DirtyList::instance()->remove(this);
	}

	return 0;
}

IndexFileHeader::IndexFileHeader(void* addr):Page(addr)
{
	fh = (FileHeader*)addr;
	LOG_PACKET("attach success \n%s",Utils::b2s((const char*)fh,sizeof(FileHeader) - sizeof(fh->reversed)).c_str());
}


string IndexFileHeader::dump()
{
	stringstream ss;
	char buf[1024] = {0};
	
	ss << "magic:" << fh->magic << endl;
	sprintf(buf,"%08X",fh->version);
	ss << "version:" << buf << endl;
	ss << "fileLength:" << fh->fileLength << endl;
	ss << "freeHead:" << fh->freeHead << endl;
	ss << "root:" << fh->root << endl;
	ss << "leafHead:" << fh->leafHead << endl;
	ss << "count:" << fh->count << endl;
	ss << "height:" << fh->height << endl;
	ss << "freePageCount:" << fh->freePageCount << endl;
	ss << "nextFileSeq:" << fh->nextFileSeq<<endl;
	return ss.str();
}


IndexFilePage::IndexFilePage(void* addr):Page(addr)
{
	node = (NODE*)addr;

	//add to lruList
	LRUList::instance()->update(this);
}


/*
int IndexFilePage::insert(uint64_t msgId,uint32_t t)
{
	if(ph->c >= PER_PAGE_ITEM_COUNT){
		LOG_WARNING("page is full,current count %d",ph->c);
		return 1;
	}

	int pos = PER_PAGE_ITEM_COUNT - (++ ph->c);
	if(ph->c == 1 || t >= di[pos + 1].time){
		di[pos].msgId = msgId;
		di[pos].time = t;
		pollute();
		return 0;
	}

	++pos;
	int high = 0, low=ph->c-2,mid=0;
	while(high <= low){
		mid = (high + low)  / 2;
		if(di[pos+mid].time > t && ( pos+mid+1 >=PER_PAGE_ITEM_COUNT || di[pos+mid+1].time <= t)){
			break;
		}

		if(di[pos+mid+1].time > t){
			high = mid + 1;
		}
		else{
			low = mid - 1;
		}
		
	}

	memmove(di+pos-1,di+pos,(mid+1)*sizeof(DataItem));

	pos += mid;
	di[pos].msgId = msgId;
	di[pos].time = t;
	
	pollute();
	
	return 0;
}

int IndexFilePage::get(int start,int limit,vector<uint64_t>& idVec)
{
	if(start < 0 || limit < 0 || start+limit > PER_PAGE_ITEM_COUNT){
		LOG_WARNING("illegal param %d %d",start,limit);
		return -1;
	}

	int startPos = PER_PAGE_ITEM_COUNT - ph->c + start;
	for(int i = 0; i < limit ; ++i){
		idVec.push_back(di[startPos+i].msgId);
	}

	return 0;
}
*/

string IndexFilePage::dump(const char* keyConfig,bool detail)
{
	stringstream ss;
	char buf[1024];
	KeyHelper kh;
	kh.init(keyConfig);
	
	int type = getType();
	if(type == IndexFilePage::EMPTY){

		if(detail)
		{
			ss << "type:free" << endl;
			ss << "next:" << node->parent << endl;
		}
	}
	else if(type == IndexFilePage::LEAF){

		if(detail)
		{
			ss << "type:leaf-node" << endl;
			ss << "parent:" << node->parent << endl;
			ss << "left:" << node->left << endl;
			ss << "right:" << node->right << endl;
			ss << "num:" << (int)node->num << endl;
		}

		
		for(int i = 0; i < node->num; ++i){
			void* key = (char*)node->data + i * (kh.getKeyLen() + 8);
			uint64_t* msgId = (uint64_t*)((char*)node->data + i * (kh.getKeyLen() + 8) + kh.getKeyLen());

			ss << kh.format(key,buf) << " " << *msgId << endl;
		}
	}
	else if(type == IndexFilePage::INNER){

		if(detail)
		{
			ss << "type:inner-node" << endl;
			ss << "parent:" << node->parent << endl;
			ss << "num:" << (int)node->num << endl;
		}
		
		for(int i = 0; i < node->num; ++i){
			void* key = (char*)node->data + i * (kh.getKeyLen() + 8);
			uint64_t* child = (uint64_t*)((char*)node->data + i * (kh.getKeyLen() + 8) + kh.getKeyLen());

			ss << kh.format(key,buf) << " " << *child << endl;
		}
	}
	return ss.str();
}

